1 module hip.bind.interpreters.lua; 2 3 version(Have_bindbc_lua): 4 private enum valid = true; 5 private enum invalid = false; 6 7 import std.traits:Unqual; 8 enum isUnsigned(T) = is(T == ubyte) || is(T == ushort) || is(T == uint) || (is(T == ulong)); 9 enum isSigned(T) = is(T == byte) || is(T == short) || is(T == int) || (is(T == long)); 10 enum isInteger(T) = isUnsigned!T || isSigned!T; 11 enum isFloating(T) = is(T == float) || is(T == double); 12 enum isNumber(T) = isInteger!T || isFloating!T; 13 enum isStructPointer(T) = is(typeof(*T.init) == struct); 14 15 16 import hip.bind.interpreters; 17 18 19 private enum LUA_TOP = -1; 20 import bindbc.lua; 21 22 alias LuaVoid = InterpreterVoid; 23 24 alias LuaFunction = extern(C) nothrow int function(lua_State* L); 25 26 void luaPushVar(T)(lua_State* L, T arg) 27 { 28 static if(isUnsigned!T) 29 lua_pushunsigned(L, arg); 30 else static if(isSigned!T) 31 lua_pushinteger(L, arg); 32 else static if(isFloating!T) 33 lua_pushnumber(L, arg); 34 else static if(is(T == bool)) 35 lua_pushboolean(L, cast(int)arg); 36 else static if(is(T == typeof(null))) 37 lua_pushnil(L); 38 else static if(is(T == string)) 39 lua_pushlstring(L, arg.ptr, arg.length); 40 else static if(is(T == struct)) 41 { 42 import core.stdc.string; 43 void* data = lua_newuserdata(L, arg.sizeof); 44 memcpy(data, &arg, arg.sizeof); 45 } 46 else //Push classes, pointers and interfaces 47 lua_pushlightuserdata(L, cast(void*)arg); 48 } 49 50 51 T luaGetFromIndex(T)(lua_State* L, int ind) 52 { 53 static if(isInteger!T) 54 { 55 assert(lua_isinteger(L, ind), "Tried to get a "~T.stringof~" from a not integer"); 56 lua_Integer i = lua_tointeger(L, ind); 57 return cast(T)i; 58 } 59 else static if(isFloating!T) 60 { 61 assert(lua_isnumber(L, ind), "Tried to get a "~T.stringof~" from a not number"); 62 lua_Number n = lua_tonumber(L, ind); 63 return cast(T)n; 64 } 65 else static if(is(T == string)) 66 { 67 import core.stdc.string; 68 assert(lua_isstring(L, ind), "Tried to get a "~T.stringof~" from a not string"); 69 auto luaStr = lua_tostring(L, ind); 70 char[] str = new char[](strlen(luaStr)); 71 return cast(T)str; 72 } 73 else static if(isStructPointer!T) 74 { 75 assert(lua_isuserdata(L, ind), "Tried to get a "~T.stringof~" from a not userdata"); 76 void* data = lua_touserdata(L, ind); 77 return cast(T)data; 78 } 79 else static if(is(T == struct)) 80 { 81 import core.stdc.string; 82 assert(lua_isuserdata(L, ind), "Tried to get a "~T.stringof~" from a not userdata"); 83 void* data = lua_touserdata(L, ind); 84 //Remove constness as it is copying 85 T ret; 86 import core.internal.traits; 87 memcpy(&ret, data, T.sizeof); 88 return ret; 89 } 90 else static if(is(T == class) || is(T == interface)) 91 { 92 assert(lua_islightuserdata(L, ind), "Tried to get a "~T.stringof~" from a not light user data"); 93 return cast(T)lua_topointer(L, ind); 94 } 95 } 96 97 98 InterpreterResult!T luaCallFunc(T, Args...)(lua_State* L, string funcName, Args args) 99 { 100 lua_getglobal(L, (funcName~'\0').ptr); 101 assert(lua_isfunction(L, LUA_TOP), "Variable "~funcName~" is not a function"); 102 static foreach(a; args) 103 push(a); 104 105 static if(is(T == class) || is(T == struct)) 106 enum returnCount = cast(int)__traits(allMembers, T).length; 107 else static if(is(T == LuaVoid)) 108 enum returnCount = cast(int)0; 109 else 110 enum returnCount = cast(int)1; 111 112 if(lua_pcall(L, args.length, returnCount, 0) != LUA_OK) 113 { 114 static if(is(T == LuaVoid)) 115 return InterpreterResult!T(invalid, LuaVoid(invalid)); 116 else 117 return InterpreterResult!T(invalid, T.init); 118 } 119 120 121 static if(is(T == LuaVoid)) 122 return InterpreterResult!T(valid, LuaVoid(valid)); 123 else static if(is(T == struct)) 124 { 125 T ret; 126 static foreach(i, m; __traits(allMembers, T)) 127 { 128 __traits(getMember, ret, m) = getFromIndex!(typeof(__traits(getMember, T, m)))(-(returnCount - cast(int)i)); 129 } 130 131 return InterpreterResult!T(valid, ret); 132 } 133 else 134 return InterpreterResult!T(valid, getFromIndex!T(LUA_TOP)); 135 } 136 137 extern(C) int externLua(alias Func)(lua_State* L) nothrow 138 { 139 import std.traits:Parameters, ReturnType; 140 import std.meta:staticMap; 141 142 staticMap!(Unqual,Parameters!Func) params; 143 int stackCounter = 0; 144 145 foreach_reverse(ref param; params) 146 { 147 stackCounter--; 148 param = luaGetFromIndex!(typeof(param))(L, stackCounter); 149 } 150 151 try 152 { 153 static if(is(ReturnType!Func == void)) 154 { 155 Func(params); 156 return 0; 157 } 158 else 159 { 160 luaPushVar(L, Func(params)); 161 return 1; 162 } 163 } 164 catch(Exception e) 165 { 166 luaPushVar(L, null); 167 try{luaL_error(L, ("A D function threw: "~e.toString~'\0').ptr);} 168 catch(Exception e){luaL_error(L, "D threw when stringifying exception");} 169 return 1; 170 } 171 } 172 173 174 class LuaInterpreter : IHipInterpreter 175 { 176 public lua_State* L; 177 private string currentFile; 178 private LuaFunction[string] funcList; 179 180 public this() 181 { 182 L = luaL_newstate(); 183 //Open standard libs 184 luaL_openlibs(L); 185 } 186 187 public bool loadFile(string fileName) 188 { 189 currentFile = fileName; 190 if(luaL_dofile(L, (fileName~"\0").ptr) != LUA_OK) 191 { 192 luaL_error(L, "HipremeEngine Lua interpreter error: %s:\n", lua_tostring(L, -1)); 193 return false; 194 } 195 return true; 196 } 197 pragma(inline) void checkLuaState() 198 { 199 assert(L != null,"No Lua State is loaded"); 200 } 201 public void reload() 202 { 203 checkLuaState(); 204 loadFile(currentFile); 205 } 206 207 void push(T)(T arg) 208 { 209 checkLuaState(); 210 luaPushVar(L, arg); 211 } 212 213 private T getFromIndex(T)(int ind) 214 { 215 checkLuaState(); 216 return luaGetFromIndex!T(L, ind); 217 } 218 219 public InterpreterResult!T call(T, Args...)(string funcName, Args args) 220 { 221 checkLuaState(); 222 return luaCallFunc!T(L, funcName, args); 223 } 224 public void expose(string name, InterpreterCFunction func) 225 { 226 expose(name, cast(LuaFunction)func); 227 } 228 public void expose(string name, LuaFunction func) 229 { 230 immutable(char)* fName; 231 fName = (name~'\0').ptr; 232 funcList[name] = func; 233 234 checkLuaState(); 235 lua_pushcfunction(L, func); 236 lua_setglobal(L, fName); 237 238 } 239 public bool hasFunction(string funcName) 240 { 241 checkLuaState(); 242 lua_getglobal(L, (funcName~'\0').ptr); 243 return lua_isfunction(L, LUA_TOP); 244 } 245 246 /** 247 * Use void to get a table 248 */ 249 public T get(T)(string varName) 250 { 251 checkLuaState(); 252 if(lua_getglobal(L, (varName~'\0').ptr) != LUA_OK) 253 { 254 luaL_error(L, "Variable not existent"); 255 } 256 static if(!is(T == void)) 257 return getFromIndex!T(LUA_TOP); 258 } 259 public void getTable(string tableName){return get!void(tableName);} 260 public T getTableField(T)(string field, int ind) 261 { 262 assert(lua_istable(L, ind), "Index on lua stack is not a table"); 263 assert(lua_getfield(L, ind, (field~'\0').ptr) == LUA_OK, "Could not get field named '"~field~"' from table"); 264 return getFromIndex!T(field, LUA_TOP); 265 } 266 267 268 ~this(){if(L != null)lua_close(L);L = null;} 269 270 271 alias L this; 272 }